HEX
Server: Apache/2.4.68 (Debian)
System: Linux as-cs-widget-demo-us-central1 6.1.0-44-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.164-1 (2026-03-09) x86_64
User: root (0)
PHP: 8.2.32
Disabled: NONE
Upload Files
File: /var/www/kevin-demo/wp-content/plugins/grocerslist/includes/Admin/SalesPage.php
<?php

namespace GrocersList\Admin;

use GrocersList\Support\Logger;
use GrocersList\Support\SalesPagePattern;

class SalesPage
{
    public const OPTION_PAGE_ID = 'grocerslist_sales_page_id';
    public const OPTION_MENU_ITEM_ID = 'grocerslist_sales_page_menu_item_id';

    private SalesPagePattern $pattern;

    public function __construct(SalesPagePattern $pattern)
    {
        $this->pattern = $pattern;
    }

    public function register(): void
    {
        add_action('init', [$this, 'registerPattern']);
    }

    public function registerPattern(): void
    {
        $this->pattern->registerPattern();
    }

    public function getState(): array
    {
        $pageId = (int) get_option(self::OPTION_PAGE_ID, 0);
        $menuItemId = (int) get_option(self::OPTION_MENU_ITEM_ID, 0);

        $page = null;
        if ($pageId > 0) {
            $post = get_post($pageId);
            if ($post && $post->post_type === 'page' && $post->post_status !== 'trash') {
                $page = [
                    'id'         => (int) $post->ID,
                    'slug'       => $post->post_name,
                    'title'      => $post->post_title,
                    'status'     => $post->post_status,
                    'editUrl'    => admin_url('post.php?post=' . $post->ID . '&action=edit'),
                    'previewUrl' => get_preview_post_link($post->ID),
                    'viewUrl'    => get_permalink($post->ID),
                ];
            } else {
                $pageId = 0;
                delete_option(self::OPTION_PAGE_ID);
            }
        }

        $menuItemLabel = '';
        if ($menuItemId > 0) {
            $menuItemPost = get_post($menuItemId);
            if ($menuItemPost) {
                $menuItemLabel = (string) $menuItemPost->post_title;
            } else {
                // stale option: menu item was deleted out-of-band (e.g. via Appearance → Menus)
                $menuItemId = 0;
                delete_option(self::OPTION_MENU_ITEM_ID);
            }
        }

        $menus = [];
        if (function_exists('wp_get_nav_menus')) {
            foreach (wp_get_nav_menus() as $menu) {
                $menus[] = [
                    'id'   => (int) $menu->term_id,
                    'name' => $menu->name,
                ];
            }
        }

        $primaryMenuId = 0;
        if (function_exists('has_nav_menu') && has_nav_menu('primary')) {
            $locations = get_nav_menu_locations();
            if (!empty($locations['primary'])) {
                $primaryMenuId = (int) $locations['primary'];
            }
        }

        return [
            'page'              => $page,
            'menuItemId'        => $menuItemId,
            'menuItemLabel'     => $menuItemLabel,
            'menus'             => $menus,
            'primaryMenuId'     => $primaryMenuId,
            'isBlockTheme'      => function_exists('wp_is_block_theme') && wp_is_block_theme(),
            'menuEditorUrl'     => admin_url('nav-menus.php'),
            'siteEditorUrl'     => admin_url('site-editor.php'),
            'supportsPattern'   => function_exists('register_block_pattern'),
        ];
    }

    /**
     * Create a draft sales page with the rendered pattern content.
     */
    public function createPage(string $slug): array
    {
        $slug = sanitize_title($slug);
        if ($slug === '') {
            $slug = 'membership';
        }

        $content = $this->pattern->buildContent();

        $pageId = wp_insert_post([
            'post_title'   => 'Membership',
            'post_name'    => $slug,
            'post_content' => $content,
            'post_status'  => 'draft',
            'post_type'    => 'page',
        ], true);

        if (is_wp_error($pageId)) {
            Logger::debug('SalesPage::createPage failed: ' . $pageId->get_error_message());
            return ['error' => $pageId->get_error_message()];
        }

        update_option(self::OPTION_PAGE_ID, (int) $pageId);

        return ['pageId' => (int) $pageId];
    }

    /**
     * Move the current sales page to the Trash and create a new draft in its place,
     * so creators can recover earlier edits via WP's Trash UI. Menu items we created
     * are hard-deleted (they're navigation pointers, not user-authored content).
     */
    public function regeneratePage(string $slug): array
    {
        $existingId = (int) get_option(self::OPTION_PAGE_ID, 0);
        if ($existingId > 0) {
            wp_delete_post($existingId, false);
            delete_option(self::OPTION_PAGE_ID);
        }

        $existingMenuItemId = (int) get_option(self::OPTION_MENU_ITEM_ID, 0);
        if ($existingMenuItemId > 0) {
            wp_delete_post($existingMenuItemId, true);
            delete_option(self::OPTION_MENU_ITEM_ID);
        }

        return $this->createPage($slug);
    }

    public function addToMenu(int $menuId, string $label): array
    {
        $pageId = (int) get_option(self::OPTION_PAGE_ID, 0);
        if ($pageId <= 0) {
            return ['error' => 'No sales page exists yet'];
        }

        if ($menuId <= 0 || !wp_get_nav_menu_object($menuId)) {
            return ['error' => 'Invalid menu'];
        }

        $existingMenuItemId = (int) get_option(self::OPTION_MENU_ITEM_ID, 0);
        if ($existingMenuItemId > 0 && get_post($existingMenuItemId)) {
            return ['menuItemId' => $existingMenuItemId];
        }

        $url = get_permalink($pageId);
        $label = sanitize_text_field($label);
        if ($label === '') {
            $label = 'Membership';
        }

        $menuItemId = wp_update_nav_menu_item($menuId, 0, [
            'menu-item-title'   => $label,
            'menu-item-url'     => $url,
            'menu-item-status'  => 'publish',
            'menu-item-type'    => 'custom',
        ]);

        if (is_wp_error($menuItemId) || !$menuItemId) {
            $msg = is_wp_error($menuItemId) ? $menuItemId->get_error_message() : 'Failed to add menu item';
            Logger::debug('SalesPage::addToMenu failed: ' . $msg);
            return ['error' => $msg];
        }

        update_option(self::OPTION_MENU_ITEM_ID, (int) $menuItemId);

        return ['menuItemId' => (int) $menuItemId];
    }

    /**
     * Update the title of the existing nav menu item without touching its
     * position, parent, or any other field. wp_update_nav_menu_item resets
     * unspecified fields to defaults, so we re-pass every persisted field
     * from the current menu item alongside the new title.
     */
    public function updateMenuItemLabel(string $label): array
    {
        $menuItemId = (int) get_option(self::OPTION_MENU_ITEM_ID, 0);
        if ($menuItemId <= 0) {
            return ['error' => 'No menu item to update'];
        }

        $post = get_post($menuItemId);
        if (!$post) {
            // stale option pointing at a deleted item; reconcile and bail
            delete_option(self::OPTION_MENU_ITEM_ID);
            return ['error' => 'No menu item to update'];
        }

        $label = sanitize_text_field($label);
        if ($label === '') {
            return ['error' => 'Label cannot be empty'];
        }

        $item = wp_setup_nav_menu_item($post);

        $menuTerms = wp_get_post_terms($menuItemId, 'nav_menu', ['fields' => 'ids']);
        if (is_wp_error($menuTerms) || empty($menuTerms)) {
            return ['error' => 'Menu item is not attached to a menu'];
        }
        $menuId = (int) $menuTerms[0];

        $args = [
            'menu-item-title'       => $label,
            'menu-item-url'         => isset($item->url) ? (string) $item->url : '',
            'menu-item-status'      => isset($item->post_status) ? (string) $item->post_status : 'publish',
            'menu-item-type'        => isset($item->type) ? (string) $item->type : 'custom',
            'menu-item-object'      => isset($item->object) ? (string) $item->object : '',
            'menu-item-object-id'   => isset($item->object_id) ? (int) $item->object_id : 0,
            'menu-item-parent-id'   => isset($item->menu_item_parent) ? (int) $item->menu_item_parent : 0,
            'menu-item-position'    => isset($item->menu_order) ? (int) $item->menu_order : 0,
            'menu-item-target'      => isset($item->target) ? (string) $item->target : '',
            'menu-item-classes'     => isset($item->classes) ? (is_array($item->classes) ? implode(' ', $item->classes) : (string) $item->classes) : '',
            'menu-item-xfn'         => isset($item->xfn) ? (string) $item->xfn : '',
            'menu-item-attr-title'  => isset($item->attr_title) ? (string) $item->attr_title : '',
            'menu-item-description' => isset($item->description) ? (string) $item->description : '',
        ];

        $result = wp_update_nav_menu_item($menuId, $menuItemId, $args);

        if (is_wp_error($result) || !$result) {
            $msg = is_wp_error($result) ? $result->get_error_message() : 'Failed to update menu item';
            Logger::debug('SalesPage::updateMenuItemLabel failed: ' . $msg);
            return ['error' => $msg];
        }

        return ['menuItemId' => (int) $result];
    }

    public function removeFromMenu(): array
    {
        $this->deleteMenuItemIfPresent();
        return ['ok' => true];
    }

    /**
     * Move the sales page to Trash and remove the menu item we added, so
     * creators can recover edits via Pages → Trash if they change their mind.
     */
    public function removePage(): array
    {
        $pageId = (int) get_option(self::OPTION_PAGE_ID, 0);
        if ($pageId > 0) {
            wp_delete_post($pageId, false);
            delete_option(self::OPTION_PAGE_ID);
        }

        $this->deleteMenuItemIfPresent();

        return ['ok' => true];
    }

    private function deleteMenuItemIfPresent(): void
    {
        $menuItemId = (int) get_option(self::OPTION_MENU_ITEM_ID, 0);
        if ($menuItemId > 0) {
            wp_delete_post($menuItemId, true);
            delete_option(self::OPTION_MENU_ITEM_ID);
        }
    }
}